package org.example;

import org.objectweb.asm.*;

import java.io.IOException;


public class Generator {

    public static byte[] getBytecodeForClass() {
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC, "org/Test", null, "org/example/Example", null);
        cw.visitField(Opcodes.ACC_PRIVATE, "name", "Ljava/lang/String;",
                null, null).visitEnd();

        MethodVisitor methodVisitor = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
        methodVisitor.visitCode();
        methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
        methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, "org/example/Example", "<init>", "()V");
        methodVisitor.visitInsn(Opcodes.RETURN);
        methodVisitor.visitMaxs(1, 1);
        methodVisitor.visitEnd();


        methodVisitor = cw.visitMethod(Opcodes.ACC_PUBLIC, "run", "(Ljava/lang/String;)V", null, null);
        methodVisitor.visitCode();
        methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J");
        methodVisitor.visitVarInsn(Opcodes.LSTORE, 2);
        methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
        methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
        methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, "org/example/Example", "run", "(Ljava/lang/String;)V");
        methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J");
        methodVisitor.visitVarInsn(Opcodes.LLOAD, 2);
        methodVisitor.visitInsn(Opcodes.LSUB);
        methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(J)V");
        methodVisitor.visitInsn(Opcodes.RETURN);
        methodVisitor.visitMaxs(5, 4);
        methodVisitor.visitEnd();

        cw.visitEnd();
        return cw.toByteArray();
    }


    public static byte[] getModifedClass() throws IOException {
        ClassReader cr = new ClassReader("org.example.Example");
        ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
        ClassVisitor cv = new ReplaceStaticMethodClassAdapter(cw);
        cr.accept(cv, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
        return cw.toByteArray();
    }

    private static class ReplaceStaticMethodClassAdapter extends ClassAdapter {


        public ReplaceStaticMethodClassAdapter(ClassVisitor classVisitor) {
            super(classVisitor);
        }

        @Override
        public MethodVisitor visitMethod(int i, String s, String s1, String s2, String[] strings) {
            return new RepalceStaticMethodAdapter(super.visitMethod(i, s, s1, s2, strings));
        }

        private class RepalceStaticMethodAdapter extends MethodAdapter {

            public RepalceStaticMethodAdapter(MethodVisitor methodVisitor) {
                super(methodVisitor);
            }

            @Override
            public void visitMethodInsn(int i, String s, String s1, String s2) {
                if (i == Opcodes.INVOKESTATIC && "java/lang/System".equals(s) && "currentTimeMillis".equals(s1) && "()J".equals(s2)) {
                    super.visitMethodInsn(Opcodes.INVOKESTATIC, "org/example/Generator", "myTime", "()J");
                } else {
                    super.visitMethodInsn(i, s, s1, s2);
                }
            }
        }
    }

    public static long myTime() {
        return 1000000L;
    }
}
